/*
 * Copyright (c) 2012 Tomas Shestakov. <http://code.google.com/p/jmmo/>
 */

package jmmo.engine.property

/**
 * A Special `Property` which linked to other one (parent).
 * `LinkedProperty` value is the same as parent value.
 * If parent `Property` value changes `LinkedProperty` value changes too and event posted.
 * For instances creation it is recommended to use `LinkedProperty` or `LinkedPropertyAttr` object.
 * @param newLink linked parent `Property`
 * @param id unique identifier of `Property`
 * @tparam A type of `Property` value
 * @author Tomas Shestakov
 */
class LinkedProperty[A](newLink: Property[A] = null, val id: Identifier = Identifier.empty) extends PropertySupport[A] {
  this.link = newLink

  def isWritable = false

  def isReadable = link ne null

  def apply() =
    if (!isReadable) throw new UnsupportedOperationException(this + " not linked")
    else link.apply()

  /**
   * Because `LinkedProperty` is not writable `update` method invocation cause [[java.lang.UnsupportedOperationException]]
   * @param value new Property value
   */
  def update(value: A) {
    throw new UnsupportedOperationException(this + " is read only")
  }

  /**
   * @return linked parent `Property`
   */
  def link = l

  /**
   * Sets specified linked parent `Property`
   * @param newLink linked parent `Property`
   */
  def link_=(newLink: Property[A]) {
    if (link ne null) link.removeChangeListener(changeListener)

    l = newLink

    if (link ne null) link.addChangeListener(changeListener)
  }

  /**
   * Contains link value
   */
  private[this] var l: Property[A] = null

  /**
   * Listener to listen parent value changes
   */
  protected val changeListener: ChangeListeners.Listener = {
    case event: ChangePropertyEvent[A] => fireChangeEvent(ChangePropertyEvent(this, event.oldValue, event.newValue))
  }

  override def canEqual(other: Any) = other.isInstanceOf[LinkedProperty[A]]

  override def equals(other: Any) = other match {
    case that: LinkedProperty[A] => (that canEqual this) && id == that.id && link == that.link
    case _ => false
  }

  override def hashCode =
    41 * (
      41 + id.##
    ) + link.##

  override def toString = "LinkedProperty(" + link + "," + id + ")"

  def alter = jmmo.util.emptyPartial[Alteration]
}

/**
 * Contains factory methods to create instances of [[jmmo.engine.property.LinkedProperty]].
 * Extractor method `unapply` extract only Property `id` and `link` without `attributes`.
 */
object LinkedProperty {

  /**
   * Creates a `RegularProperty` with specified link, id and attributes.
   * @param link linked parent `Property`
   * @param id unique identifier of `Property`
   * @tparam A type of `Property` value
   */
  def apply[A](link: Property[A] = null, id: Identifier = Identifier.empty) = new LinkedProperty[A](link, id)

  /**
   * This extractor method is called in a pattern match { case LinkedProperty(...) => }.
   * @param property
   * @tparam A type of `Property` value
   * @return Property `id` and `link` wrapped in an Option.
   */
  def unapply[A](property: LinkedProperty[A]): Option[(Property[A], Identifier)] =
    if (property eq null) None
    else Some(property.link, property.id)
}
